package com.wang.webSocket;

import com.alibaba.fastjson.JSON;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import com.wang.conf.AppConf;
import com.wang.conf.AppData;
import com.wang.packpojo.ConHeart;
import com.wang.packpojo.ConMsg;
import com.wang.packpojo.DebugConMsg;
import com.wang.packpojo.MsgStatus;
import com.wang.pojo.DevConConfig;
import com.wang.pojo.Device;
import com.wang.service.DevConConfigService;
import com.wang.service.DevConConfigServiceImpl;
import com.wang.service.DeviceService;
import com.wang.service.DeviceServiceImpl;
import com.wang.socket.OutBoundHandler;
import com.wang.socket.SocketMap;
import com.wang.util.EmptyUtil;
import com.wang.util.ModBusUtil;
import com.wang.util.PrintUtil;
import com.wang.util.SpringUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.CharsetUtil;


import java.net.InetSocketAddress;
import java.util.Date;
import java.util.Map;


//单例
public class WebInBoundHandler extends SimpleChannelInboundHandler<Object> {


    private WebSocketServerHandshaker handshaker;

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {

        if (msg instanceof FullHttpRequest) {
            //以http请求形式接入，但是走的是websocket
            handleHttpRequest(ctx, (FullHttpRequest) msg);
        } else if (msg instanceof WebSocketFrame) {
            //处理websocket客户端的消息
            handlerWebSocketFrame(ctx, (WebSocketFrame) msg);
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        //添加连接
        PrintUtil.print("客户端加入连接：" + ctx.channel());

    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        //断开连接
        //断开连接
        String address = getRemoteAddress(ctx);
        String devGId = "";
        //钉钉
        Map<String, DebugConMsg> clientDDCtxMap = WebSocketMap.clientDDCtxMap;
        //查询websocket
        for (Map.Entry<String, DebugConMsg> entry : clientDDCtxMap.entrySet()) {
            String ss = entry.getKey();
            if (getRemoteAddress(entry.getValue().getWebSocketConMsg().getCtx()).equals(address)) {
                devGId = ss;
                //断开钉钉连接
                disDDConnectDev(devGId);
                break;
            }
        }
        //微信
        Map<String, DebugConMsg> clientWXCtxMap = WebSocketMap.clientWXCtxMap;
        //查询websocket
        for (Map.Entry<String, DebugConMsg> entry : clientWXCtxMap.entrySet()) {
            String ss = entry.getKey();
            if (getRemoteAddress(entry.getValue().getWebSocketConMsg().getCtx()).equals(address)) {
                devGId = ss;
                //断开微信连接
                disWXConnectDev(devGId);
                break;
            }
        }
        //WEB
        Map<String, DebugConMsg> clientWEBCtxMap = WebSocketMap.clientWEBCtxMap;
        //查询websocket
        for (Map.Entry<String, DebugConMsg> entry : clientWEBCtxMap.entrySet()) {
            String ss = entry.getKey();
            if (getRemoteAddress(entry.getValue().getWebSocketConMsg().getCtx()).equals(address)) {
                devGId = ss;
                //断开web连接
                disWebConnectDev(devGId);
                break;
            }
        }

        PrintUtil.print(getRemoteAddress(ctx) + "断开连接");

    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }


    //钉钉异常断开 和正常断开websocket  清除连接
    public void disDDConnectDev(String devGId) {

        PrintUtil.print("断开的连接设备：" + devGId);

        if (EmptyUtil.isNotNull(devGId)) {
            //移除客户端连接
            ChannelHandlerContext ctx = WebSocketMap.clientDDCtxMap.get(devGId).getWebSocketConMsg().getCtx();
            if (ctx != null) {
                WebSocketMap.clientDDCtxMap.remove(devGId);
                ctx.close();
                PrintUtil.print("钉钉-设备：" + devGId + ":webSocket断开成功");
            }

        }

    }

    //微信异常断开 和正常断开websocket  清除连接
    public void disWXConnectDev(String devGId) {

        PrintUtil.print("断开的连接设备：" + devGId);

        if (EmptyUtil.isNotNull(devGId)) {
            //移除客户端连接
            ChannelHandlerContext ctx = WebSocketMap.clientWXCtxMap.get(devGId).getWebSocketConMsg().getCtx();
            if (ctx != null) {
                WebSocketMap.clientWXCtxMap.remove(devGId);
                ctx.close();
                PrintUtil.print("微信-设备：" + devGId + ":webSocket断开成功");
            }
        }

    }

    //WEB异常断开 和正常断开websocket  清除连接
    public void disWebConnectDev(String devGId) {

        PrintUtil.print("断开的连接设备：" + devGId);

        if (EmptyUtil.isNotNull(devGId)) {
            //移除客户端连接
            ChannelHandlerContext ctx = WebSocketMap.clientWEBCtxMap.get(devGId).getWebSocketConMsg().getCtx();
            if (ctx != null) {
                WebSocketMap.clientWEBCtxMap.remove(devGId);
                ctx.close();
                PrintUtil.print("WEB-设备：" + devGId + ":webSocket断开成功");
            }
        }

    }

    private void handlerWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {


        // 判断是否关闭链路的指令
        if (frame instanceof CloseWebSocketFrame) {
            handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
            return;
        }
        // 判断是否ping消息
        if (frame instanceof PingWebSocketFrame) {
            ctx.channel().write(
                    new PongWebSocketFrame(frame.content().retain()));
            return;
        }
        // 本例程仅支持文本消息，不支持二进制消息
        if (!(frame instanceof TextWebSocketFrame)) {
            //PrintUtil.print("本例程仅支持文本消息，不支持二进制消息");
            throw new UnsupportedOperationException(String.format(
                    "%s frame types not supported", frame.getClass().getName()));
        }
        // 返回应答消息
        String address = getRemoteAddress(ctx);

        String request = ((TextWebSocketFrame) frame).text();
        PrintUtil.print("服务端收到：" + address + ":" + request);

        //处理数据
        //判断消息
        try {
            dataHander(request, ctx);
        } catch (Exception e) {
            e.printStackTrace();
            //MsgStatus msg2 = new MsgStatus("", "", "error", "reply", "数据格式异常", "");
            //WebOutBoundHander.outData(ctx, JSON.toJSONString(msg2));
            //System.out.println("发送成功");
            PrintUtil.print("数据格式错误");
        }

    }

    /**
     * 唯一的一次http请求，用于创建websocket
     */
    private void handleHttpRequest(ChannelHandlerContext ctx,
                                   FullHttpRequest req) {


        //要求Upgrade为websocket，过滤掉get/Post
        if (!req.decoderResult().isSuccess()
                || (!"websocket".equals(req.headers().get("Upgrade")))) {
            //若不是websocket方式，则创建BAD_REQUEST的req，返回给客户端
            sendHttpResponse(ctx, req, new DefaultFullHttpResponse(
                    HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
            return;
        }
        WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
                "ws://localhost:9905/websocket", null, false);
        handshaker = wsFactory.newHandshaker(req);
        if (handshaker == null) {
            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());

        } else {
            handshaker.handshake(ctx.channel(), req);
        }


    }

    /**
     * 拒绝不合法的请求，并返回错误信息
     */
    private static void sendHttpResponse(ChannelHandlerContext ctx,
                                         FullHttpRequest req, DefaultFullHttpResponse res) {
        // 返回应答给客户端
        if (res.status().code() != 200) {
            ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(),
                    CharsetUtil.UTF_8);
            res.content().writeBytes(buf);
            buf.release();
        }
        ChannelFuture f = ctx.channel().writeAndFlush(res);
        // 如果是非Keep-Alive，关闭连接

        if (!isKeepAlive(req) || res.status().code() != 200) {
            f.addListener(ChannelFutureListener.CLOSE);
        }
    }

    private static boolean isKeepAlive(FullHttpRequest req) {
        return false;
    }


    public void dataHander(String data, ChannelHandlerContext ctx) throws Exception {

        String address = getRemoteAddress(ctx);
        if (data != null) {

            MsgStatus msg = null;
            try {
                PrintUtil.print("收到数据:" + data);
                msg = JSON.parseObject(data, MsgStatus.class);
            } catch (Exception e) {
                // e.printStackTrace();
                PrintUtil.print("接收数据转换异常！！");
            }

            if (msg != null) {


                String devGId = msg.getDevGId();
                //判断传输数据类型 modbus 还是json
                DeviceService deviceService = SpringUtil.getBean("deviceServiceImpl", DeviceServiceImpl.class);
                Device device = deviceService.selectDeviceByDevGId(devGId);
                DevConConfigService devConConfigService = SpringUtil.getBean("devConConfigServiceImpl", DevConConfigServiceImpl.class);
                DevConConfig devConConfig = devConConfigService.selectConfigByDevId(device.getDevId());

                //解析命令类型
                switch (msg.getCommand()) {
                    case "connect_DD":   //钉钉测试连接


                        //判断目前是否已经包含了连接
                        PrintUtil.print("钉钉connect：" + address);
                        if (WebSocketMap.clientDDCtxMap != null && WebSocketMap.clientDDCtxMap.containsKey(devGId)) {
                            MsgStatus disMsg = new MsgStatus(devGId, "disconnect", "error", "reply", "异常断开，已有新的客户端进行连接", "");
                            PrintUtil.print("钉钉客户端：" + devGId + "重复连接、踢除连接");
                            //发送
                            WebOutBoundHander.outData(WebSocketMap.clientDDCtxMap.get(devGId).getWebSocketConMsg().getCtx(), JSON.toJSONString(disMsg));
                            //断开连接
                            channelInactive(WebSocketMap.clientDDCtxMap.get(devGId).getWebSocketConMsg().getCtx());
                        }

                        //添加列表
                        DebugConMsg debugConMsg = new DebugConMsg();

                        //webSocket连接信息
                        ConMsg ddConMsg = new ConMsg();
                        Device ddDevice = new Device();
                        ddDevice.setDevGId(devGId);
                        ddConMsg.setDevice(ddDevice);
                        ddConMsg.setCtx(ctx);
                        //添加心跳包
                        ConHeart ddConHeart = new ConHeart();
                        ddConMsg.setConHeart(ddConHeart);
                        ddConMsg.setConnectTime(new Date().getTime());
                        debugConMsg.setWebSocketConMsg(ddConMsg);

                        //如果是modbus
                        if (devConConfig.getDevDataTypeCode().equals(AppData.ModbusDataCode)) {
                            //socket 连接信息
                            for (Map.Entry<String, ConMsg> entry : SocketMap.mRegPassDeviceMap.entrySet()) {
                                if (entry.getValue().getDevice().getDevGId().equals(devGId)) {
                                    debugConMsg.setSocketConMsg(entry.getValue());
                                    break;
                                }
                            }
                        } else { //如果是json
                            //socket 连接信息
                            for (Map.Entry<String, ConMsg> entry : SocketMap.jRegPassDeviceMap.entrySet()) {
                                if (entry.getValue().getDevice().getDevGId().equals(devGId)) {
                                    debugConMsg.setSocketConMsg(entry.getValue());
                                    break;
                                }
                            }
                        }


                        WebSocketMap.clientDDCtxMap.put(devGId, debugConMsg);

                        MsgStatus ddConMsgData = new MsgStatus(msg.getDevGId(), "connect", "success", "reply", "连接成功", "");
                        PrintUtil.print("钉钉客户端：" + msg.getDevGId() + "连接成功");
                        WebOutBoundHander.outData(ctx, JSON.toJSONString(ddConMsgData));
                        break;
                /*    case "connect_WX":   //微信连接
                        //判断目前是否已经包含了连接
                        PrintUtil.print("微信connect：" + address);
                        if (WebSocketMap.clientWXCtxMap != null && WebSocketMap.clientWXCtxMap.containsKey(msg.getDevGId())) {
                            MsgStatus disMsg = new MsgStatus(msg.getDevGId(), "disconnect", "error", "reply", "异常断开，已有新的客户端进行连接", "");
                            PrintUtil.print("微信客户端：" + msg.getDevGId() + "重复连接、踢除连接");
                            //发送
                            WebOutBoundHander.outData(WebSocketMap.clientWXCtxMap.get(msg.getDevGId()).getCtx(), JSON.toJSONString(disMsg));
                            //断开连接
                            channelInactive(WebSocketMap.clientWXCtxMap.get(msg.getDevGId()).getCtx());
                        }

                        //添加列表
                        ConMsg wxConMsg = new ConMsg();
                        Device wxDevice = new Device();
                        wxDevice.setDevGId(msg.getDevGId());
                        wxConMsg.setDevice(wxDevice);
                        wxConMsg.setCtx(ctx);
                        //添加心跳包
                        ConHeart wxConHeart = new ConHeart();
                        wxConMsg.setConHeart(wxConHeart);
                        wxConMsg.setConnectTime(new Date().getTime());


                        WebSocketMap.clientWXCtxMap.put(msg.getDevGId(), wxConMsg);
                        MsgStatus wxConMsgData = new MsgStatus(msg.getDevGId(), "connect", "success", "reply", "连接成功", "");
                        PrintUtil.print("微信客户端：" + msg.getDevGId() + "连接成功");
                        WebOutBoundHander.outData(ctx, JSON.toJSONString(wxConMsgData));
                        break;*/
                    case "connect_WEB":   //web连接
                        //判断目前是否已经包含了连接
                        PrintUtil.print("WEB connect：" + address);

                        if (WebSocketMap.clientWEBCtxMap != null && WebSocketMap.clientWEBCtxMap.containsKey(devGId)) {
                            MsgStatus disMsg = new MsgStatus(msg.getDevGId(), "disconnect", "error", "reply", "异常断开，已有新的客户端进行连接", "");
                            PrintUtil.print("WEB客户端：" + msg.getDevGId() + "重复连接、踢除连接");
                            //发送
                            WebOutBoundHander.outData(WebSocketMap.clientWEBCtxMap.get(devGId).getWebSocketConMsg().getCtx(), JSON.toJSONString(disMsg));
                            //断开连接
                            channelInactive(WebSocketMap.clientWEBCtxMap.get(devGId).getWebSocketConMsg().getCtx());
                        }


                        //添加列表
                        DebugConMsg webDebugConMsg = new DebugConMsg();


                        //添加列表
                        ConMsg webConMsg = new ConMsg();
                        Device webDevice = new Device();
                        webDevice.setDevGId(devGId);
                        webConMsg.setDevice(webDevice);
                        webConMsg.setCtx(ctx);
                        //添加心跳包
                        ConHeart webConHeart = new ConHeart();
                        webConMsg.setConHeart(webConHeart);
                        webConMsg.setConnectTime(new Date().getTime());


                        webDebugConMsg.setWebSocketConMsg(webConMsg);

                        //如果是modbus
                        if (devConConfig.getDevDataTypeCode().equals(AppData.ModbusDataCode)) {
                            //socket 连接信息
                            for (Map.Entry<String, ConMsg> entry : SocketMap.mRegPassDeviceMap.entrySet()) {
                                if (entry.getValue().getDevice().getDevGId().equals(devGId)) {
                                    webDebugConMsg.setSocketConMsg(entry.getValue());
                                    break;
                                }
                            }
                        } else {  //如果是json
                            //socket 连接信息
                            for (Map.Entry<String, ConMsg> entry : SocketMap.jRegPassDeviceMap.entrySet()) {
                                if (entry.getValue().getDevice().getDevGId().equals(devGId)) {
                                    webDebugConMsg.setSocketConMsg(entry.getValue());
                                    break;
                                }
                            }
                        }


                        WebSocketMap.clientWEBCtxMap.put(devGId, webDebugConMsg);
                        MsgStatus webConMsgData = new MsgStatus(msg.getDevGId(), "connect", "success", "reply", "连接成功", "");
                        PrintUtil.print("web客户端：" + msg.getDevGId() + "连接成功");
                        WebOutBoundHander.outData(ctx, JSON.toJSONString(webConMsgData));
                        break;
                    case "setCollectTime_DD":
                        //判断目前是否已经包含了连接
                        if (WebSocketMap.clientDDCtxMap != null && WebSocketMap.clientDDCtxMap.containsKey(msg.getDevGId())) {
                            //获取设备连接
                            ConMsg socketConMsg = WebSocketMap.clientDDCtxMap.get(msg.getDevGId()).getSocketConMsg();

                            //如果是modbus
                            if (devConConfig.getDevDataTypeCode().equals(AppData.ModbusDataCode)) {

                            } else {
                                if (socketConMsg != null) {
                                    ChannelHandlerContext ctx1 = socketConMsg.getCtx();
                                    msg.setCommandType("cUploadInterval");
                                    msg.setCommandType("reply");
                                    OutBoundHandler.outData(ctx1, JSON.toJSONString(msg));
                                }
                            }

                        }
                        break;

                    case "heartBeat_DD":  //钉钉心跳包
                        //心跳包
                        if (WebSocketMap.clientDDCtxMap != null && WebSocketMap.clientDDCtxMap.containsKey(msg.getDevGId())) {
                            if (msg.getResult().equals("success")) {
                                ConHeart conHeart = WebSocketMap.clientDDCtxMap.get(msg.getDevGId()).getWebSocketConMsg().getConHeart();
                                conHeart.setReceiveTime(new Date());
                                PrintUtil.print("心跳包-钉钉：" + msg.getDevGId());
                            }
                        }
                        break;
                    case "heartBeat_WX":  //微信心跳包
                        //心跳包
                        if (WebSocketMap.clientWXCtxMap != null && WebSocketMap.clientWXCtxMap.containsKey(msg.getDevGId())) {
                            if (msg.getResult().equals("success")) {
                                ConHeart conHeart = WebSocketMap.clientWXCtxMap.get(msg.getDevGId()).getWebSocketConMsg().getConHeart();
                                conHeart.setReceiveTime(new Date());
                                PrintUtil.print("心跳包-微信：" + msg.getDevGId());

                            }
                        }
                        break;
                    case "heartBeat_WEB":  //微信心跳包
                        //心跳包
                        if (WebSocketMap.clientWEBCtxMap != null && WebSocketMap.clientWEBCtxMap.containsKey(msg.getDevGId())) {
                            if (msg.getResult().equals("success")) {
                                ConHeart conHeart = WebSocketMap.clientWEBCtxMap.get(msg.getDevGId()).getWebSocketConMsg().getConHeart();
                                conHeart.setReceiveTime(new Date());
                                PrintUtil.print("心跳包-WEB：" + msg.getDevGId());

                            }
                        }
                        break;
                /*    case "getDevCheckData_DD":  //主动获取检测数据  暂时改成http
                        if (WebSocketMap.clientDDCtxMap != null && WebSocketMap.clientDDCtxMap.containsKey(msg.getDevGId())) {

                            //获取设备连接
                            ConMsg socketConMsg = WebSocketMap.clientDDCtxMap.get(msg.getDevGId()).getSocketConMsg();



                            //如果是modbus
                            if(devConConfig.getDevDataTypeCode().equals(AppData.ModbusDataCode)){
                                PrintUtil.print("钉钉测试端主动获取数据：设备编号："+msg.getDevGId());
                                sendModbusCommand(socketConMsg);
                            }else{
                                if(socketConMsg!=null) {
                                    ChannelHandlerContext ctx1 = socketConMsg.getCtx();
                                    msg.setCommandType("getDevCheckData");
                                    msg.setCommandType("reply");
                                    OutBoundHandler.outData(ctx1, JSON.toJSONString(msg));
                                }
                            }

                        }
                        break;*/
                   /* case "getDevCheckData_WEB":  //主动获取检测数据
                        if (WebSocketMap.clientWEBCtxMap != null && WebSocketMap.clientWEBCtxMap.containsKey(msg.getDevGId())) {
                            //获取设备连接
                            ConMsg socketConMsg1 = WebSocketMap.clientDDCtxMap.get(msg.getDevGId()).getSocketConMsg();
                            //如果是modbus
                            if(devConConfig.getDevDataTypeCode().equals(AppData.ModbusDataCode)){
                                sendModbusCommand(socketConMsg1);
                            }else{
                                ChannelHandlerContext ctx1 =   socketConMsg1.getCtx();
                                msg.setCommandType("getDevCheckData");
                                msg.setCommandType("reply");
                                OutBoundHandler.outData(ctx1,JSON.toJSONString(msg));
                            }

                        }
                        break;*/
               /* case "setMqtt_tencent_WX": //发送腾讯设置
                case "setMqtt_baidu_WX": //发送百度设置
                case "setMqtt_ali_WX": //发送阿里设置
                case "setMqtt_other_WX": //发送阿里设置

                    //获取mqtt
                    //判断目前是否已经包含了连接
                    if(WebSocketMap.clientWXCtxMap!=null &&WebSocketMap.clientWXCtxMap.containsKey(msg.getDevGId())){
                        msg.setCommandType("reply");
                        String  setMqttData = JSON.toJSONString(msg);
                        MQTTServer.sendMQTTMessage(msg.getDevGId(), MQTTConnect.topic+msg.getDevGId(),setMqttData);
                    }
                    break;*/


                    default:
                        break;
                }
            }
        }
    }

    public String getRemoteAddress(ChannelHandlerContext ctx) {
        InetSocketAddress ipSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = ipSocket.getAddress().getHostAddress();
        Integer port = ipSocket.getPort();
        String address = clientIp + ":" + port;
        return address;
    }

    public void sendModbusCommand(ConMsg socketConMsg) {


        new Thread(() -> {
            //发送数据
            try {
                //String 地址位 String 指令
                Map<String, String> commandMap = socketConMsg.getCommandMap();
                //如果指令数量大于1 ，则发数据 建立全局变量 等待全部存储
                if (commandMap != null && commandMap.size() > 1) {
                    for (Map.Entry<String, String> entry1 : commandMap.entrySet()) {
                        //发送指令
                        byte[] commandByte = ModBusUtil.HexString2Bytes(entry1.getValue());
                        OutBoundHandler.outData(socketConMsg.getCtx(), commandByte);

                        PrintUtil.print("发送：" + (socketConMsg.getDevice().getDevGId() + " : " + entry1.getValue()));

                        Device device = socketConMsg.getDevice();

                        WebSocketUtil.sendTestData(device.getDevGId(), entry1.getValue());

                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                    }
                } else if (commandMap != null && commandMap.size() == 1) {
                    //如果指令数量等于1, 则直接 发送命令 存储数据
                    String command = "";
                    for (Map.Entry<String, String> entry2 : commandMap.entrySet()) {

                        command = entry2.getValue();
                    }
                    //去除所有的空格
                    byte[] commandByte = ModBusUtil.HexString2Bytes(command);

                    OutBoundHandler.outData(socketConMsg.getCtx(), commandByte);
                    PrintUtil.print("发送：" + (socketConMsg.getDevice().getDevGId() + " : " + command));

                    WebSocketUtil.sendTestData(socketConMsg.getDevice().getDevGId(), command);


                }

            } catch (Exception e) {
                e.printStackTrace();
            }

        }).start();

    }

}